package net.fukure.android.pecabc;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import net.fukure.android.pecabc.http.HttpResponse;
import net.fukure.android.pecabc.http.HttpUtil;
import net.fukure.android.pecabc.listener.OnPeercastListener;

import org.apache.http.message.BasicHeader;
import org.apache.http.Header;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import android.net.Uri;
import android.util.Log;

public class Peercast {

	final static String LOG_TAG = "AndroidFFWMEPeercast";
	
    private List<OnPeercastListener> listeners = new ArrayList<OnPeercastListener>();
    private Thread watcher = null;
    private long latest = 0;
    
	final static String ADMIN_URL_PATH = "/admin";
	final static String RELAY_URL_PATH = "/html/ja/relays.html";
	
	private String host = "localhost:7146";
	private String pass = "test";
	
	private String broadcast_url;
	private String broadcast_chname;
	private String broadcast_desc;
	private String broadcast_contact;
	private String broadcast_genre;
	private String broadcast_id;
	//private String broadcast_listeners;
	private String peercast_session_id;
	
	public Peercast() {
	}
	
	public void updateXmlStatus(){
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					Thread.sleep(1500);
					if(System.currentTimeMillis() - latest < 10000) return;
					latest = System.currentTimeMillis();
					String xmlstr = getAdminXml();
					broadcast_id = getBCIDFromName(broadcast_chname, xmlstr);
					//broadcast_listeners = getListenersFromID(broadcast_id, xmlstr);
				} catch (Exception e) {
					broadcast_id = null;
					//broadcast_listeners = "0";
				}

	        	firePeercastEvent("onPeercastUpdateStatus");
				Log.d(LOG_TAG, "broadcast_id:"+broadcast_id);
			}
		}).start();
	}
	
	public void watchstart(){
		if(watcher!=null){
			watcher.interrupt();
		}
		watcher = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while(true){
						updateXmlStatus();
						Thread.sleep(30*1000);
					}
				} catch (InterruptedException e) {
				}
			}
		});
		watcher.start();
	}
	
	public void watchend(){
		if(watcher!=null){
			watcher.interrupt();
		}
	}
	
	public boolean isBroadcasting(){
		if(broadcast_id==null){
			return false;
		}else{
			return true;
		}
	}
	
	private String getAdminXml(){
		Uri.Builder builder = new Uri.Builder();
		builder.scheme("http");
		builder.encodedAuthority(host);
		builder.path(ADMIN_URL_PATH);
		builder.appendQueryParameter("cmd", "viewxml");
		String url = builder.build().toString();

		Header[] headers = new Header[1];
		headers[0] = new BasicHeader("Cookie", "id="+peercast_session_id);
		
		HttpResponse response = HttpUtil.get(url, headers);
		return response.body;
	}

	public void addPeercastListener(OnPeercastListener listener){
		listeners.remove(listener);
		listeners.add(listener);
	}

	public void removePeercastListener(OnPeercastListener listener){
		listeners.remove(listener);
	}

	private void firePeercastEvent(String type){
		for (int i = 0; i < listeners.size(); i++) {
			OnPeercastListener listener = listeners.get(i);
			if(type.endsWith("LoginOk")) listener.onPeercastLoginOk();
			if(type.endsWith("LoginError")) listener.onPeercastLoginError();
			if(type.endsWith("StartOk")) listener.onPeercastStartOk();
			if(type.endsWith("StartError")) listener.onPeercastStartError();
			if(type.endsWith("StopOk")) listener.onPeercastStopOk();
			if(type.endsWith("StopError")) listener.onPeercastStopError();
			if(type.endsWith("UpdateStatus")) listener.onPeercastUpdateStatus();
		}
	}
	
	public void changeSetting(String host, String password){
		this.host = host;
		this.pass = password;
	}
	
	private String getIDFromHeaders(Header[] headers){
		for (int i = 0; i < headers.length; i++) {
			Header header = headers[i];
			if(header.getName().equals("Set-Cookie")){
				String cookie = header.getValue();
				String[] pair = cookie.split(";")[0].split("=");
				if(pair[0].trim().equals("id")){
					return pair[1].trim();
				}
			}
		}
		return "";
	}
	/*
	private String getBCIDFromHTML(String html){
		String id = null;
		String regex = "id=([0-9A-F]{32}).*?>BROADCAST<";
		Pattern p = Pattern.compile(regex, Pattern.DOTALL);
	
		Matcher m = p.matcher(html);
		if (m.find()&&m.groupCount()>0){
		  id = m.group(1);
		}
		Log.d(LOG_TAG, "id="+id);
		
		return id;
	}
	*/
	public void setBroadcastParam(String streamUrl, String chname, String desc, String genre, String contact) {
		this.broadcast_url = streamUrl;
		this.broadcast_chname = chname;
		this.broadcast_desc = desc;
		this.broadcast_genre = genre;
		this.broadcast_contact = contact;
	}
	
	public String getBroadcastParamContact(){
		return broadcast_contact;
	}

	public boolean login(){

		String url = "http://"+host+ADMIN_URL_PATH;
		url += "?cmd=login&pass="+pass;

		HttpResponse response = HttpUtil.get(url);
		Log.d(LOG_TAG, "login status " + response.status);
		
        String id = getIDFromHeaders(response.headers);
        if(id!=null){
        	peercast_session_id = id;
        	firePeercastEvent("onPeercastLoginOk");
			Log.d(LOG_TAG, "login ok "+id);
			return true;
		}else{
        	firePeercastEvent("onPeercastLoginError");
			Log.e(LOG_TAG, "login error");
			return false;
		}

	}
	
	public boolean broadcast(){
		
		Uri.Builder builder = new Uri.Builder();
		builder.scheme("http");
		builder.encodedAuthority(host);
		builder.path(ADMIN_URL_PATH);
		builder.appendQueryParameter("cmd", "fetch");
		builder.appendQueryParameter("url", broadcast_url);
		builder.appendQueryParameter("name", broadcast_chname);
		builder.appendQueryParameter("desc", broadcast_desc);
		builder.appendQueryParameter("genre", broadcast_genre);
		builder.appendQueryParameter("contact", broadcast_contact);
		builder.appendQueryParameter("bitrate", "");
		builder.appendQueryParameter("type", "WMV");
		String url = builder.build().toString();
		
		Header[] headers = new Header[1];
		headers[0] = new BasicHeader("Cookie", "id="+peercast_session_id);
		
		HttpResponse response = HttpUtil.get(url, headers);

		updateXmlStatus();
		
		Log.d(LOG_TAG, "start status " + response.status);
		if(0<response.status && response.status < 400){
			firePeercastEvent("onPeercastStartOk");
			Log.d(LOG_TAG, "start ok");
			return true;
		}else{
			firePeercastEvent("onPeercastStartError");
			Log.e(LOG_TAG, "start error");
			return false;
		}
		
	}
	
	public void broadend(){
		if(broadcast_id!=null){
    		broadend(broadcast_id);
    		updateXmlStatus();
		}else{
			firePeercastEvent("onPeercastStopError");
			Log.e(LOG_TAG, "stop error");
		}
	}
	/*
	public void broadend(){

		String url = "http://"+host+RELAY_URL_PATH;
		Header[] headers = new Header[1];
		headers[0] = new BasicHeader("Cookie", "id="+peercast_session_id);
		
		HttpResponse response = HttpUtil.get(url, headers);
		
		updateXmlStatus();
		
		Log.d(LOG_TAG, "stop relay status " + response.status);
		
		String id = getBCIDFromHTML(response.body);
        if(id!=null){
    		try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {}
    		broadend(id);
		}else{
			firePeercastEvent("onPeercastStopError");
			Log.e(LOG_TAG, "stop error");
		}
		
	}
	*/
	private boolean broadend(String id){

		Uri.Builder builder = new Uri.Builder();
		builder.scheme("http");
		builder.encodedAuthority(host);
		builder.path(ADMIN_URL_PATH);
		builder.appendQueryParameter("cmd", "stop");
		builder.appendQueryParameter("id", id);
		String url = builder.build().toString();
		
		Header[] headers = new Header[1];
		headers[0] = new BasicHeader("Cookie", "id="+peercast_session_id);
		
		HttpResponse response = HttpUtil.get(url, headers);
		
		Log.d(LOG_TAG, "stop status " + response.status);
        if(0 < response.status && response.status < 400){
        	firePeercastEvent("onPeercastStopOk");
			Log.d(LOG_TAG, "stop ok");
			return true;
		}else{
        	firePeercastEvent("onPeercastStopError");
			Log.e(LOG_TAG, "stop error");
			return false;
		}
        
	}

	public void asyncLogin(){
		Log.d(LOG_TAG, "asyncLogin");
		new Thread(new Runnable() {
			@Override
			public void run() {
				login();
			}
		}).start();
	}
	
	public void asyncStartWithLogin(){
		Log.d(LOG_TAG, "asyncStartWithLogin");
		new Thread(new Runnable() {
			@Override
			public void run() {
				if(login()){
					broadcast();
				}
			}
		}).start();
	}
	
	public void asyncStart(){
		Log.d(LOG_TAG, "asyncStart");
		new Thread(new Runnable() {
			@Override
			public void run() {
				broadcast();
			}
		}).start();
	}

	public void asyncStop(){
		Log.d(LOG_TAG, "asyncStop");
		new Thread(new Runnable() {
			@Override
			public void run() {
				broadend();
			}
		}).start();
	}
	
	private Document str2doc(String xmlstr) throws Exception{
    	ByteArrayInputStream bais = new ByteArrayInputStream(xmlstr.getBytes("UTF-8"));
    	DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        return builder.parse(bais);
	}
	
	public String getListenersFromID(String id, String xmlstr) throws Exception{
    	
		Document document = str2doc(xmlstr);
        XPath xpath = XPathFactory.newInstance().newXPath();
        
        Node ch = (Node)xpath.evaluate("/peercast/channels_found/channel[@id=\""+id+"\"]", document, XPathConstants.NODE);
        String listeners = xpath.evaluate("./hits/@listeners", ch);
        //String relays = xpath.evaluate("./hits/@relays", ch);
        //String status = xpath.evaluate("/peercast/channels_relayed/channel[@id=\""+id+"\"]/relay/@status", document);
        return listeners;
	}
	
    public String getBCIDFromName(String bcname, String xmlstr) throws Exception {

    	Document document = str2doc(xmlstr);
        XPath xpath = XPathFactory.newInstance().newXPath();
        
        String exp = "/peercast/channels_relayed/channel[@name=\""+bcname+"\"]/relay[@status=\"BROADCAST\"]";
        Node relay = (Node)xpath.evaluate(exp, document, XPathConstants.NODE);
        Node ch = relay.getParentNode();
        return xpath.evaluate("./@id", ch);
        
    }
	
}